﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using OpenTKLightingTool.Helper;
using OpenTKLightingTool.Data;
using YamlDotNet.Serialization;
using Ignitron.EEngine;
using Ignitron.EEngine.Lighting;
using Ignitron.EEngine.Maths;

namespace OpenTKLightingTool.Yaml
{
    internal class ExportImport
    {
        /// <summary>
        /// Exports a dataStorage to yaml
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="storage"></param>
        public void Save(string filePath, DataStorage storage)
        {
            // create the anonymous object
            var data = new
            {
                Lighting = new {
                    GeneralAmbient = Vector4DTO.From(storage.Lighting.GlobalAmbient),
                    LocalViewer = storage.Lighting.LocalViewer,
                    TwoSide = storage.Lighting.TwoSided
                }, 

                Materials = storage.Materials.Select(m => new 
                    {
                        Key = m.Key,
                        Ambient = Vector4DTO.From(m.Value.Ambient),
                        Diffuse = Vector4DTO.From(m.Value.Diffuse),
                        Specular = Vector4DTO.From(m.Value.Specular),
                        Emissive = Vector4DTO.From(m.Value.Emissive),
                        Shininess = m.Value.Shininess

                    }).ToList(),

                Lights = storage.Lights.Select(l => new
                {
                    IsEnabled = l.IsEnabled,
                    Position = Vector4DTO.From(l.Position),
                    
                    Ambient = Vector4DTO.From(l.Ambient),
                    Diffuse = Vector4DTO.From(l.Diffuse),
                    Specular =  Vector4DTO.From(l.Specular),

                    ConstantAttenuation = l.ConstantAttenuation,
                    LinearAttenuation = l.LinearAttenuation,
                    QuadraticAttenuation = l.QuadraticAttenuation,

                    SpotCutOff = l.SpotCutOff,
                    SpotDirection = Vector4DTO.From(l.SpotDirection),
                    SpotExponent = l.SpotExponent
                }).ToList()
            };

            using(FileStream stream = new FileStream(filePath, FileMode.Create))
            {
                StreamWriter writer = new StreamWriter(stream);
                var serializer = new Serializer(SerializationOptions.EmitDefaults);
                serializer.Serialize(writer, data);
                writer.Flush();
                stream.Flush();
                stream.Close();
            }                     
        }

        /// <summary>
        /// Imports the data storage from the file
        /// </summary>
        /// <param name="filePath">File to load</param>
        /// <param name="ds">data storage passed</param>
        public void Load(string filePath, DataStorage ds, IEngine engine)
        {
            // deserialize the object graph
            dynamic objGraph;
            using (FileStream stream = new FileStream(filePath, FileMode.Open))
            {
                StreamReader reader = new StreamReader(stream);
                var deserializer = new Deserializer();
                objGraph = deserializer.Deserialize(reader);
                stream.Close();
            }

            // parse lighting
            dynamic objLighting = objGraph["Lighting"];
            ds.Lighting = new Lighting()
            {
                GlobalAmbient = Vector4DTO.To(objLighting["GeneralAmbient"]),
                LocalViewer = Convert.ToBoolean(objLighting["LocalViewer"]),
                TwoSided = Convert.ToBoolean(objLighting["TwoSide"])
            };

            // parse materials
            dynamic[] objMaterials = objGraph["Materials"].ToArray();
            ds.Materials.Clear();
            foreach (dynamic objMat in objMaterials)
            {
                ds.Materials[objMat["Key"]] = new Material()
                {
                    Ambient = Vector4DTO.To(objMat["Ambient"]),
                    Diffuse = Vector4DTO.To(objMat["Diffuse"]),
                    Specular = Vector4DTO.To(objMat["Specular"]),
                    Emissive = Vector4DTO.To(objMat["Emissive"]),
                    Shininess = Parse.ParseFloat(objMat["Shininess"])
                };                    
            }

            // parse lights
            dynamic[] objLights = objGraph["Lights"].ToArray();
            var eLights = ds.Lights.Select(l => l.ELight).ToArray();
            ds.Lights.Clear();
            int index = 0;
            foreach (dynamic objLight in objLights)
            {
                var light = new Light(engine, eLights[index])
                {
                    IsEnabled = Convert.ToBoolean(objLight["IsEnabled"]),
                    Position = Vector4DTO.To(objLight["Position"]),

                    Ambient = Vector4DTO.To(objLight["Ambient"]),
                    Diffuse = Vector4DTO.To(objLight["Diffuse"]),
                    Specular = Vector4DTO.To(objLight["Specular"]),

                    ConstantAttenuation = Parse.ParseFloat(objLight["ConstantAttenuation"]),
                    LinearAttenuation = Parse.ParseFloat(objLight["LinearAttenuation"]),
                    QuadraticAttenuation = Parse.ParseFloat(objLight["QuadraticAttenuation"]),

                    SpotCutOff = Parse.ParseFloat(objLight["SpotCutOff"]),
                    SpotDirection = Vector4DTO.To(objLight["SpotDirection"]),
                    SpotExponent = Parse.ParseFloat(objLight["SpotExponent"])
                };

                light.UpdateELight();
                ds.Lights.Add(light);

                index++;
            }
        }

        private class Vector4DTO
        {
            public float X
            { get; set; }

            public float Y
            { get; set; }

            public float Z
            { get; set; }

            public float W
            { get; set; }

            public static Vector4DTO From(EVector4 vec)
            {
                return new Vector4DTO() { X = vec.X, Y = vec.Y, Z = vec.Z, W = vec.W };
            }

            public static EVector4 To(dynamic obj)
            {
                return new EVector4(Parse.ParseFloat(obj["X"]), Parse.ParseFloat(obj["Y"]), Parse.ParseFloat(obj["Z"]), Parse.ParseFloat(obj["W"]));
            }
        }
    }
}
